/*-------------------------------------------------------------------------
 *
 * jsonb.h
 *	  jsonb数据类型支持的声明。
 *
 * Copyright (c) 1996-2022, PostgreSQL Global Development Group
 *
 * src/include/utils/jsonb.h
 *
 *-------------------------------------------------------------------------
 */
#ifndef __JSONB_H__
#define __JSONB_H__

#include "lib/stringinfo.h"
#include "utils/array.h"
#include "utils/numeric.h"

/* 在顺序处理 jsonb 值时使用的标记 */
typedef enum
{
	WJB_DONE,
	WJB_KEY,
	WJB_VALUE,
	WJB_ELEM,
	WJB_BEGIN_ARRAY,
	WJB_END_ARRAY,
	WJB_BEGIN_OBJECT,
	WJB_END_OBJECT
} JsonbIteratorToken;

/* GIN 索引操作类的策略编号 */
#define JsonbContainsStrategyNumber		7
#define JsonbExistsStrategyNumber		9
#define JsonbExistsAnyStrategyNumber	10
#define JsonbExistsAllStrategyNumber	11
#define JsonbJsonpathExistsStrategyNumber		15
#define JsonbJsonpathPredicateStrategyNumber	16


/*
 * 在标准 jsonb_ops GIN 操作类中，我们选择对
 * 键和值进行索引。存储格式为文本。文本
 * 字符串的第一个字节区分它是键（始终是字符串）、空值、
 * 布尔值、数值或字符串值。然而，作为字符串的数组元素
 * 被标记为键；这种不精确性支持 "exists" 运算符的定义，
 * 该运算符将数组元素视为键。文本字符串的其余部分对于
 * 空值为空，为布尔值则为 "t" 或 "f"，为数值则为数值的
 * 规范化打印表示，或为字符串值的文本。然而，如果该文本表示的
 * 长度超过 JGIN_MAXLENGTH 字节，则我们会对
 * 文本表示进行哈希，并存储 uint32 哈希值的 8 个十六进制数位的表示形式，
 * 用一个额外的位标记前缀字以区别这是发生的情况。
 * 哈希长字符串节省空间，并确保我们不会超出 GIN 索引的最大条目长度。
 * （但是 JGIN_MAXLENGTH 明显短于 GIN 的限制。它被选择
 * 是为了确保磁盘上的文本数据项将具有短的 varlena 头。）
 * 注意，当任何哈希项出现在查询中时，我们必须重新检查索引
 * 匹配与堆元组的匹配；当前，这没有任何成本，因为我们
 * 始终出于其他原因需要重新检查。
 */
#define JGINFLAG_KEY	0x01	/* 键（或字符串数组元素） */
#define JGINFLAG_NULL	0x02	/* 空值 */
#define JGINFLAG_BOOL	0x03	/* 布尔值 */
#define JGINFLAG_NUM	0x04	/* 数值 */
#define JGINFLAG_STR	0x05	/* 字符串值（如果不是数组元素） */
#define JGINFLAG_HASHED 0x10	/* 如果值被哈希，则与标志进行或运算 */
#define JGIN_MAXLENGTH	125		/* 哈希之前文本部分的最大长度 */

/* 便利宏 */
#define DatumGetJsonbP(d)	((Jsonb *) PG_DETOAST_DATUM(d))
#define DatumGetJsonbPCopy(d)	((Jsonb *) PG_DETOAST_DATUM_COPY(d))
#define JsonbPGetDatum(p)	PointerGetDatum(p)
#define PG_GETARG_JSONB_P(x)	DatumGetJsonbP(PG_GETARG_DATUM(x))
#define PG_GETARG_JSONB_P_COPY(x)	DatumGetJsonbPCopy(PG_GETARG_DATUM(x))
#define PG_RETURN_JSONB_P(x)	PG_RETURN_POINTER(x)

typedef struct JsonbPair JsonbPair;
typedef struct JsonbValue JsonbValue;

/*
 * Jsonbs 是 varlena 对象，因此必须遵循 varlena 约定，即对象的
 * 第一个 int32 包含对象的总字节大小。请务必使用 VARSIZE() 和 
 * SET_VARSIZE() 进行访问！
 *
 * Jsonb 是磁盘上的表示，与内存中的 JsonbValue 表示相对立。通常，
 * JsonbValues 只是通过它们访问的 Jsonb 缓冲区的适配器，但也可以
 * 深度复制并传递。
 *
 * Jsonb 是一种树结构。树中的每个节点由一个 JEntry 头和一个可变
 * 长度的内容（可能为零大小）组成。JEntry 头表明节点的类型，例如
 * 字符串或数组，并提供其可变长度部分的长度。
 *
 * JEntry 和节点的内容并不物理上存储在一起。相反，容器数组或对象
 * 有一个数组，保存所有子节点的 JEntrys，后跟它们的可变长度部分。
 *
 * 根节点是一个例外；它没有能够保存其 JEntry 的父数组或对象。
 * 因此，根节点不存储 JEntry 头。可以隐式知道根节点必须是数组
 * 或对象，因此只要我们能够区分二者，就不需要类型指示符。为此，
 * 数组和对象都以一个 uint32 头字段开始，该字段包含 JB_FOBJECT
 * 或 JB_FARRAY 标志。当裸标量值需要作为 Jsonb 值存储时，我们
 * 实际上存储的是一个具有一个元素的数组，数组的头字段中的标志
 * 设置为 JB_FSCALAR | JB_FARRAY。
 *
 * 总体而言，Jsonb 结构需要 4 字节对齐。在结构内部，一些节点类型
 * 的可变长度部分按照 4 字节对齐，而其他则不。当需要对齐时，填充
 * 位于节点需要对齐的开始。例如，如果一个数字节点存储在一个
 * 字符串节点之后，使得数字节点在偏移量 3 开始，则数字节点的
 * 可变长度部分将以一个填充字节开始，以便实际数字数据是 4 字节
 * 对齐的。
 */

/*
 * JEntry 格式。
 *
 * 最低有效的 28 位存储的是条目的数据长度，或者是其从
 * 所含对象的可变长度部分开始的结束+1 偏移量。接下来的三位
 * 存储条目的类型，高位位指示最低有效位存储的是长度还是
 * 偏移量。
 *
 * 使用偏移量或长度的复杂性的原因是在访问速度和数据压缩性
 * 之间进行妥协。在最初的设计中，每个 JEntry 始终存储偏移量，
 * 但这导致 JEntry 数组的压缩性属性非常糟糕，以至于 JSONB 的
 * TOAST 压缩效果不好。仅存储长度会大大提高压缩性，但会使
 * 随机访问大型数组的成本高昂（O(N) 而不是 O(1)）。因此，
 * 我们在每个 JB_OFFSET_STRIDE 的 JEntry 中存储偏移量，而在
 * 其余部分存储长度。这会导致合理的压缩数据（只要步幅不太小）。
 * 我们可能需要检查多达 JB_OFFSET_STRIDE 个 JEntry，以便
 * 找出任何给定项的偏移量或长度，但无论容器多大，仍然是 O(1)。
 *
 * 如果我们将步幅存储在容器头部，或者如果我们愿意将步幅视为
 * 不可更改的常量，我们可以避免为此目的使用一个标志位。然而，
 * 这两个选项都不是很有吸引力。
 */
typedef uint32 JEntry;

#define JENTRY_OFFLENMASK		0x0FFFFFFF
#define JENTRY_TYPEMASK			0x70000000
#define JENTRY_HAS_OFF			0x80000000

/* 存储在类型位中的值 */
#define JENTRY_ISSTRING			0x00000000
#define JENTRY_ISNUMERIC		0x10000000
#define JENTRY_ISBOOL_FALSE		0x20000000
#define JENTRY_ISBOOL_TRUE		0x30000000
#define JENTRY_ISNULL			0x40000000
#define JENTRY_ISCONTAINER		0x50000000	/* 数组或对象 */

/* 访问宏。注意可能的多次评估 */
#define JBE_OFFLENFLD(je_)		((je_) & JENTRY_OFFLENMASK)
#define JBE_HAS_OFF(je_)		(((je_) & JENTRY_HAS_OFF) != 0)
#define JBE_ISSTRING(je_)		(((je_) & JENTRY_TYPEMASK) == JENTRY_ISSTRING)
#define JBE_ISNUMERIC(je_)		(((je_) & JENTRY_TYPEMASK) == JENTRY_ISNUMERIC)
#define JBE_ISCONTAINER(je_)	(((je_) & JENTRY_TYPEMASK) == JENTRY_ISCONTAINER)
#define JBE_ISNULL(je_)			(((je_) & JENTRY_TYPEMASK) == JENTRY_ISNULL)
#define JBE_ISBOOL_TRUE(je_)	(((je_) & JENTRY_TYPEMASK) == JENTRY_ISBOOL_TRUE)
#define JBE_ISBOOL_FALSE(je_)	(((je_) & JENTRY_TYPEMASK) == JENTRY_ISBOOL_FALSE)
#define JBE_ISBOOL(je_)			(JBE_ISBOOL_TRUE(je_) || JBE_ISBOOL_FALSE(je_))

/* 将偏移变量向前推进到下一个 JEntry 的宏 */
#define JBE_ADVANCE_OFFSET(offset, je) \
	do { \
		JEntry	je_ = (je); \
		if (JBE_HAS_OFF(je_)) \
			(offset) = JBE_OFFLENFLD(je_); \
		else \
			(offset) += JBE_OFFLENFLD(je_); \
	} while(0)

/*
 * 我们存储的是偏移量，而不是长度，每个 JB_OFFSET_STRIDE 的
 * 子节点。注意：此宏仅在创建 JSONB 值时引用。当检查现有值时，
 * 请注意 HAS_OFF 位。这样可以在不破坏磁盘兼容性的情况下，
 * 允许偏移量位置启发式的改变。
 */
#define JB_OFFSET_STRIDE		32


/*
 * 一个jsonb数组或对象节点，在Jsonb Datum中。
 *
 * 一个数组为每个元素有一个子节点，按数组顺序存储。
 *
 * 一个对象为每个键/值对有两个子节点。所有键首先出现，
 * 按键排序顺序；然后值出现，顺序与键顺序匹配。这样的安排保持
 * 键在内存中的紧凑性，使得搜索特定键更加缓存友好。
 */
typedef struct JsonbContainer
{
	uint32		header;			/* 元素或键/值对的数量，以及
								 * 标志 */
	JEntry		children[FLEXIBLE_ARRAY_MEMBER];

	/* 每个子节点的数据随后跟随。 */
} JsonbContainer;

/* JsonbContainer中头字段的标志 */
#define JB_CMASK				0x0FFFFFFF	/* 计数字段的掩码 */
#define JB_FSCALAR				0x10000000	/* 标志位 */
#define JB_FOBJECT				0x20000000
#define JB_FARRAY				0x40000000

/* 方便的宏，用于访问JsonbContainer结构体 */
#define JsonContainerSize(jc)		((jc)->header & JB_CMASK)
#define JsonContainerIsScalar(jc)	(((jc)->header & JB_FSCALAR) != 0)
#define JsonContainerIsObject(jc)	(((jc)->header & JB_FOBJECT) != 0)
#define JsonContainerIsArray(jc)	(((jc)->header & JB_FARRAY) != 0)

/* jsonb datum的顶层磁盘格式。 */
typedef struct
{
	int32		vl_len_;		/* varlena 头部（请勿直接触摸！） */
	JsonbContainer root;
} Jsonb;

/* 方便的宏，用于访问Jsonb datum中的根容器 */
#define JB_ROOT_COUNT(jbp_)		(*(uint32 *) VARDATA(jbp_) & JB_CMASK)
#define JB_ROOT_IS_SCALAR(jbp_) ((*(uint32 *) VARDATA(jbp_) & JB_FSCALAR) != 0)
#define JB_ROOT_IS_OBJECT(jbp_) ((*(uint32 *) VARDATA(jbp_) & JB_FOBJECT) != 0)
#define JB_ROOT_IS_ARRAY(jbp_)	((*(uint32 *) VARDATA(jbp_) & JB_FARRAY) != 0)


enum jbvType
{
	/* 标量类型 */
	jbvNull = 0x0,
	jbvString,
	jbvNumeric,
	jbvBool,
	/* 复合类型 */
	jbvArray = 0x10,
	jbvObject,
	/* 二进制（即结构Jsonb）jbvArray/jbvObject */
	jbvBinary,

	/*
	 * 虚拟类型。
	 *
	 * 这些类型仅用于内存中的JSON处理，并在输出到json/jsonb时序列化
	 * 为JSON字符串。
	 */
	jbvDatetime = 0x20,
};

/*
 * JsonbValue：Jsonb的内存表示。这是一个方便的
 * 反序列化表示，可以轻松支持在操作过程中使用“val”
 * 联合体跨底层类型。Jsonb的磁盘
 * 表示具有各种对齐考虑。
 */
struct JsonbValue
{
	enum jbvType type;			/* 影响排序顺序 */

	union
	{
		Numeric numeric;
		bool		boolean;
		struct
		{
			int			len;
			char	   *val;	/* 不一定以null结尾 */
		}			string;		/* 字符串原始类型 */

		struct
		{
			int			nElems;
			JsonbValue *elems;
			bool		rawScalar;	/* 顶层 "原始标量" 数组? */
		}			array;		/* 数组容器类型 */

		struct
		{
			int			nPairs; /* 1 对，2 个元素 */
			JsonbPair  *pairs;
		}			object;		/* 关联容器类型 */

		struct
		{
			int			len;
			JsonbContainer *data;
		}			binary;		/* 数组或对象，以磁盘格式存储 */

		struct
		{
			Datum		value;
			Oid			typid;
			int32		typmod;
			int			tz;		/* 数字时区，以秒为单位，适用于
								 * TimestampTz 数据类型 */
		}			datetime;
	}			val;
};

#define IsAJsonbScalar(jsonbval)	(((jsonbval)->type >= jbvNull && \
									  (jsonbval)->type <= jbvBool) || \
									  (jsonbval)->type == jbvDatetime)

/*
 * 对象中的键/值对。
 *
 * 该结构类型仅在构建 Jsonb 时短暂使用；它不是
 * * 磁盘表示。
 *
 * 重复键的对会被去重。我们存储最初观察到的
 * 对的顺序，以便以明确定义的方式删除重复项（即“最后观察到的获胜”）。
 */
struct JsonbPair
{
	JsonbValue	key;			/* 必须是 jbvString */
	JsonbValue	value;			/* 可以是任何类型 */
	uint32		order;			/* 对应于原始序列中的对的索引 */
};

/* 解析 Jsonb 时使用的转换状态，或用于类型强制转换 */
typedef struct JsonbParseState
{
	JsonbValue	contVal;
	Size		size;
	struct JsonbParseState *next;
} JsonbParseState;

/*
 * JsonbIterator 持有每个迭代的类型详细信息。它还存储一个
 * Jsonb varlena 缓冲区，在某些上下文中可以直接访问。
 */
typedef enum
{
	JBI_ARRAY_START,
	JBI_ARRAY_ELEM,
	JBI_OBJECT_START,
	JBI_OBJECT_KEY,
	JBI_OBJECT_VALUE
} JsonbIterState;

typedef struct JsonbIterator
{
	/* 正在迭代的容器 */
	JsonbContainer *container;
	uint32		nElems;			/* 子数组中的元素数量（为对象时将是 nPairs） */
	bool		isScalar;		/* 伪数组标量值? */
	JEntry	   *children;		/* 子节点的 JEntrys */
	/* 数据主体。 这指向可变长度数据的开头 */
	char	   *dataProper;

	/* 当前缓冲区中的项目（最多 nElems） */
	int			curIndex;

	/* 与当前项目对应的数据偏移量 */
	uint32		curDataOffset;

	/*
	 * 如果容器是一个对象，我们希望交替返回键和值；
	 * 因此 curDataOffset 指向当前键，curValueOffset 指向当前值。
	 */
	uint32		curValueOffset;

	/* 私有状态 */
	JsonbIterState state;

	struct JsonbIterator *parent;
} JsonbIterator;


/* 支持函数 */
extern uint32 getJsonbOffset(const JsonbContainer *jc, int index);
extern uint32 getJsonbLength(const JsonbContainer *jc, int index);
extern int	compareJsonbContainers(JsonbContainer *a, JsonbContainer *b);
extern JsonbValue *findJsonbValueFromContainer(JsonbContainer *sheader,
											   uint32 flags,
											   JsonbValue *key);
extern JsonbValue *getKeyJsonValueFromContainer(JsonbContainer *container,
												const char *keyVal, int keyLen,
												JsonbValue *res);
extern JsonbValue *getIthJsonbValueFromContainer(JsonbContainer *sheader,
												 uint32 i);
extern JsonbValue *pushJsonbValue(JsonbParseState **pstate,
								  JsonbIteratorToken seq, JsonbValue *jbval);
extern JsonbIterator *JsonbIteratorInit(JsonbContainer *container);
extern JsonbIteratorToken JsonbIteratorNext(JsonbIterator **it, JsonbValue *val,
											bool skipNested);
extern void JsonbToJsonbValue(Jsonb *jsonb, JsonbValue *val);
extern Jsonb *JsonbValueToJsonb(JsonbValue *val);
extern bool JsonbDeepContains(JsonbIterator **val,
							  JsonbIterator **mContained);
extern void JsonbHashScalarValue(const JsonbValue *scalarVal, uint32 *hash);
extern void JsonbHashScalarValueExtended(const JsonbValue *scalarVal,
										 uint64 *hash, uint64 seed);

/* jsonb.c 支持函数 */
extern char *JsonbToCString(StringInfo out, JsonbContainer *in,
							int estimated_len);
extern char *JsonbToCStringIndent(StringInfo out, JsonbContainer *in,
								  int estimated_len);
extern bool JsonbExtractScalar(JsonbContainer *jbc, JsonbValue *res);
extern const char *JsonbTypeName(JsonbValue *jb);

extern Datum jsonb_set_element(Jsonb *jb, Datum *path, int path_len,
							   JsonbValue *newval);
extern Datum jsonb_get_element(Jsonb *jb, Datum *path, int npath,
							   bool *isnull, bool as_text);
#endif							/* __JSONB_H__ */
